package com.macro.mall.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.json.JSONUtil;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.common.utils.BinaryUtil;
import com.aliyun.oss.model.MatchMode;
import com.aliyun.oss.model.PolicyConditions;
import com.macro.mall.dto.*;
import com.macro.mall.exception.ApiException;
import com.macro.mall.service.OssService;
import io.minio.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * Oss对象存储管理Service实现类
 * Created by macro on 2018/5/17.
 */
@Service
public class OssServiceImpl implements OssService {

	private static final Logger LOGGER = LoggerFactory.getLogger(OssServiceImpl.class);
	@Value("${aliyun.oss.policy.expire}")
	private int ALIYUN_OSS_EXPIRE;
	@Value("${aliyun.oss.maxSize}")
	private int ALIYUN_OSS_MAX_SIZE;
	@Value("${aliyun.oss.callback}")
	private String ALIYUN_OSS_CALLBACK;
	@Value("${aliyun.oss.bucketName}")
	private String ALIYUN_OSS_BUCKET_NAME;
	@Value("${aliyun.oss.endpoint}")
	private String ALIYUN_OSS_ENDPOINT;
	@Value("${aliyun.oss.dir.prefix}")
	private String ALIYUN_OSS_DIR_PREFIX;

	@Autowired
	private OSSClient ossClient;

	/**
	 * 签名生成
	 */
	@Override
	public OssPolicyResult policy() {
		OssPolicyResult result = new OssPolicyResult();
		// 存储目录
		SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
		String dir = ALIYUN_OSS_DIR_PREFIX+sdf.format(new Date());
		// 签名有效期
		long expireEndTime = System.currentTimeMillis() + ALIYUN_OSS_EXPIRE * 1000;
		Date expiration = new Date(expireEndTime);
		// 文件大小
		long maxSize = ALIYUN_OSS_MAX_SIZE * 1024 * 1024;
		// 回调
		OssCallbackParam callback = new OssCallbackParam();
		callback.setCallbackUrl(ALIYUN_OSS_CALLBACK);
		callback.setCallbackBody("filename=${object}&size=${size}&mimeType=${mimeType}&height=${imageInfo.height}&width=${imageInfo.width}");
		callback.setCallbackBodyType("application/x-www-form-urlencoded");
		// 提交节点
		String action = "http://" + ALIYUN_OSS_BUCKET_NAME + "." + ALIYUN_OSS_ENDPOINT;
		try {
			PolicyConditions policyConds = new PolicyConditions();
			policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, maxSize);
			policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir);
			String postPolicy = ossClient.generatePostPolicy(expiration, policyConds);
			byte[] binaryData = postPolicy.getBytes("utf-8");
			String policy = BinaryUtil.toBase64String(binaryData);
			String signature = ossClient.calculatePostSignature(postPolicy);
			String callbackData = BinaryUtil.toBase64String(JSONUtil.parse(callback).toString().getBytes("utf-8"));
			// 返回结果
			result.setAccessKeyId(ossClient.getCredentialsProvider().getCredentials().getAccessKeyId());
			result.setPolicy(policy);
			result.setSignature(signature);
			result.setDir(dir);
			result.setCallback(callbackData);
			result.setHost(action);
		} catch (Exception e) {
			LOGGER.error("签名生成失败", e);
		}
		return result;
	}

	@Override
	public OssCallbackResult callback(HttpServletRequest request) {
		OssCallbackResult result= new OssCallbackResult();
		String filename = request.getParameter("filename");
		filename = "http://".concat(ALIYUN_OSS_BUCKET_NAME).concat(".").concat(ALIYUN_OSS_ENDPOINT).concat("/").concat(filename);
		result.setFilename(filename);
		result.setSize(request.getParameter("size"));
		result.setMimeType(request.getParameter("mimeType"));
		result.setWidth(request.getParameter("width"));
		result.setHeight(request.getParameter("height"));
		return result;
	}

	@Value("${minio.endpoint}")
	private String ENDPOINT;
	@Value("${minio.bucketName}")
	private String BUCKET_NAME;
	@Value("${minio.accessKey}")
	private String ACCESS_KEY;
	@Value("${minio.secretKey}")
	private String SECRET_KEY;

	@Override
	public MinioUploadDto upload(MultipartFile file) {
		try {
			//创建一个MinIO的Java客户端
			MinioClient minioClient =MinioClient.builder()
					.endpoint(ENDPOINT)
					.credentials(ACCESS_KEY,SECRET_KEY)
					.build();
			boolean isExist = minioClient.bucketExists(BucketExistsArgs.builder().bucket(BUCKET_NAME).build());
			if (isExist) {
				LOGGER.info("存储桶已经存在！");
			} else {
				//创建存储桶并设置只读权限
				minioClient.makeBucket(MakeBucketArgs.builder().bucket(BUCKET_NAME).build());
				BucketPolicyConfigDto bucketPolicyConfigDto = createBucketPolicyConfigDto(BUCKET_NAME);
				SetBucketPolicyArgs setBucketPolicyArgs = SetBucketPolicyArgs.builder()
						.bucket(BUCKET_NAME)
						.config(JSONUtil.toJsonStr(bucketPolicyConfigDto))
						.build();
				minioClient.setBucketPolicy(setBucketPolicyArgs);
			}
			String filename = file.getOriginalFilename();
			SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
			// 设置存储对象名称
			String objectName = sdf.format(new Date()) + "/" + filename;
			// 使用putObject上传一个文件到存储桶中
			PutObjectArgs putObjectArgs = PutObjectArgs.builder()
					.bucket(BUCKET_NAME)
					.object(objectName)
					.contentType(file.getContentType())
					.stream(file.getInputStream(), file.getSize(), ObjectWriteArgs.MIN_MULTIPART_SIZE).build();
			minioClient.putObject(putObjectArgs);
			LOGGER.info("文件上传成功!");
			MinioUploadDto minioUploadDto = new MinioUploadDto();
			minioUploadDto.setName(filename);
			minioUploadDto.setUrl(ENDPOINT + "/" + BUCKET_NAME + "/" + objectName);
			return minioUploadDto;
		} catch (Exception e) {
			e.printStackTrace();
			LOGGER.info("上传发生错误: {}！", e.getMessage());
		}
		throw new ApiException("1320301");
	}
	private BucketPolicyConfigDto createBucketPolicyConfigDto(String bucketName) {
		BucketPolicyConfigDto.Statement statement = BucketPolicyConfigDto.Statement.builder()
				.Effect("Allow")
				.Principal("*")
				.Action("s3:GetObject")
				.Resource("arn:aws:s3:::"+bucketName+"/*.**").build();
		return BucketPolicyConfigDto.builder()
				.Version("2012-10-17")
				.Statement(CollUtil.toList(statement))
				.build();
	}

}
